using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Security.Permissions;
using System.Diagnostics;
using System.Windows.Forms;
using System.Security;

namespace DaveSexton.AutoInputProtection.Install.Actions
{
	[RunInstaller(true)]
	public sealed partial class InstallWebControlInVisualStudio : Installer
	{
		#region Private / Protected
		private delegate void RegisterWebControl(System.Collections.IDictionary stateSaver, string source, string target);

		private readonly string AutoInputProtectionAssemblyName = "DaveSexton.AutoInputProtection.dll";
		private readonly string AutoInputProtectionXmlDocumentationFileName = "DaveSexton.AutoInputProtection.xml";
		private readonly string TabName = "DaveSexton.Web.Controls";
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="InstallWebControlInVisualStudio" /> class.
		/// </summary>
		public InstallWebControlInVisualStudio()
		{
			InitializeComponent();
		}
		#endregion

		#region Methods
		public override void Install(System.Collections.IDictionary stateSaver)
		{
			try
			{
				string reg2005 = Context.Parameters["reg2005"];
				string controls2005 = Context.Parameters["controls2005"];
				string reg2008 = Context.Parameters["reg2008"];
				string controls2008 = Context.Parameters["controls2008"];
				string target = Context.Parameters["target"];

				RegisterWebControlForVisualStudio(stateSaver, reg2005, controls2005, target, RegisterWebControlForVisualStudio2005);
				RegisterWebControlForVisualStudio(stateSaver, reg2008, controls2008, target, RegisterWebControlForVisualStudio2008);
			}
			finally
			{
				base.Install(stateSaver);
			}
		}

		public override void Rollback(System.Collections.IDictionary savedState)
		{
			try
			{
				UnregisterWebControlForVisualStudio2005(savedState);
				UnregisterWebControlForVisualStudio2008(savedState);
			}
			finally
			{
				base.Rollback(savedState);
			}
		}

		public override void Uninstall(System.Collections.IDictionary savedState)
		{
			try
			{
				UnregisterWebControlForVisualStudio2005(savedState);
				UnregisterWebControlForVisualStudio2008(savedState);
			}
			finally
			{
				base.Uninstall(savedState);
			}
		}

		private void RegisterWebControlForVisualStudio(System.Collections.IDictionary stateSaver, string reg, string controlsPath, string target, RegisterWebControl register)
		{
			if (reg == "1" && !string.IsNullOrEmpty(controlsPath))
			{
				if (string.IsNullOrEmpty(target))
					throw new InstallException("Installation path is null or empty.");

				register(stateSaver, Path.Combine(target, "bin"), Path.Combine(controlsPath, TabName));
			}
		}

		private void RegisterWebControlForVisualStudio2005(System.Collections.IDictionary stateSaver, string source, string target)
		{
			string assemblyTarget, xmlDocTarget;
			RegisterWebControlForVisualStudio(source, target, out assemblyTarget, out xmlDocTarget);

			stateSaver.Add("ControlsInstallFullPath2005", assemblyTarget);
			stateSaver.Add("ControlsInstallXmlDocFullPath2005", xmlDocTarget);

			VisualStudioInstallCommunityControls2005(true);
		}

		private void RegisterWebControlForVisualStudio2008(System.Collections.IDictionary stateSaver, string source, string target)
		{
			string assemblyTarget, xmlDocTarget;
			RegisterWebControlForVisualStudio(source, target, out assemblyTarget, out xmlDocTarget);

			stateSaver.Add("ControlsInstallFullPath2008", assemblyTarget);
			stateSaver.Add("ControlsInstallXmlDocFullPath2008", xmlDocTarget);

			VisualStudioInstallCommunityControls2008(true);
		}

		private void RegisterWebControlForVisualStudio(string installedPath, string controlsPath, out string assemblyTarget, out string xmlDocTarget)
		{
			try
			{
				if (!Directory.Exists(controlsPath))
				{
					try
					{
						Directory.CreateDirectory(controlsPath);
					}
					catch (IOException ex)
					{
						throw new InstallException("A file system exception has prevented the target directory for the AutoInputProtection control assembly from being created: " + controlsPath, ex);
					}
					catch (UnauthorizedAccessException ex)
					{
						throw new InstallException("The current user is not authorized to create the target directory for the AutoInputProtection assembly: " + controlsPath, ex);
					}
					catch (NotSupportedException ex)
					{
						throw new InstallException("The target directory for the AutoInputProtection assembly cannot be created: " + controlsPath, ex);
					}
					catch (ArgumentException ex)
					{
						throw new InstallException("The target directory for the AutoInputProtection assembly is invalid: " + controlsPath, ex);
					}
				}

				string source = Path.Combine(installedPath, AutoInputProtectionAssemblyName);
				assemblyTarget = Path.Combine(controlsPath, AutoInputProtectionAssemblyName);

				try
				{
					File.Copy(source, assemblyTarget, true);
				}
				catch (FileNotFoundException ex)
				{
					throw new InstallException("Missing source AutoInputProtection assembly." +
						Environment.NewLine + "source: " + source + Environment.NewLine + "target: " + assemblyTarget, ex);
				}
				catch (IOException ex)
				{
					throw new InstallException("A file system exception has prevented the AutoInputProtection assembly from being copied." +
						Environment.NewLine + "source: " + source + Environment.NewLine + "target: " + assemblyTarget, ex);
				}
				catch (UnauthorizedAccessException ex)
				{
					throw new InstallException("The current user is not authorized to copy the AutoInputProtection assembly." +
						Environment.NewLine + "source: " + source + Environment.NewLine + "target: " + assemblyTarget, ex);
				}
				catch (NotSupportedException ex)
				{
					throw new InstallException("The AutoInputProtection assembly cannot be copied." +
						Environment.NewLine + "source: " + source + Environment.NewLine + "target: " + assemblyTarget, ex);
				}
				catch (ArgumentException ex)
				{
					throw new InstallException("Either the source, target or both paths are invalid." +
						Environment.NewLine + "source: " + source + Environment.NewLine + "target: " + assemblyTarget, ex);
				}

				source = Path.Combine(installedPath, AutoInputProtectionXmlDocumentationFileName);
				xmlDocTarget = Path.Combine(controlsPath, AutoInputProtectionXmlDocumentationFileName);

				try
				{
					File.Copy(source, xmlDocTarget, true);
				}
				// Copying the XML documentation file is not critical
				catch (IOException) { }
				catch (UnauthorizedAccessException) { }
				catch (NotSupportedException) { }
				catch (ArgumentException) { }
			}
			catch (ArgumentException ex)
			{
				throw new InstallException("One or more arguments are invalid." +
					Environment.NewLine + "installedPath: " + installedPath + Environment.NewLine + "controlsPath: " + controlsPath, ex);
			}
		}

		private void UnregisterWebControlForVisualStudio2005(System.Collections.IDictionary savedState)
		{
			string assemblyPath = savedState["ControlsInstallFullPath2005"] as string;
			string xmlDocPath = savedState["ControlsInstallXmlDocFullPath2005"] as string;

			if (!string.IsNullOrEmpty(assemblyPath))
			{
				UnregisterWebControlForVisualStudio(assemblyPath, xmlDocPath);

				// this method also uninstalls the controls from the toolbox
				VisualStudioInstallCommunityControls2005(false);
			}
		}

		private void UnregisterWebControlForVisualStudio2008(System.Collections.IDictionary savedState)
		{
			string assemblyPath = savedState["ControlsInstallFullPath2008"] as string;
			string xmlDocPath = savedState["ControlsInstallXmlDocFullPath2008"] as string;

			if (!string.IsNullOrEmpty(assemblyPath))
			{
				UnregisterWebControlForVisualStudio(assemblyPath, xmlDocPath);

				// this method also uninstalls the controls from the toolbox
				VisualStudioInstallCommunityControls2008(false);
			}
		}

		private void UnregisterWebControlForVisualStudio(string assemblyPath, string xmlDocPath)
		{
			try
			{
				File.Delete(assemblyPath);
			}
			catch (IOException ex)
			{
				throw new InstallException("A file system exception has prevented the AutoInputProtection assembly from being removed: " + assemblyPath, ex);
			}
			catch (UnauthorizedAccessException ex)
			{
				throw new InstallException("The current user is not authorized to remove the AutoInputProtection assembly: " + assemblyPath, ex);
			}
			catch (NotSupportedException ex)
			{
				throw new InstallException("The AutoInputProtection assembly cannot be removed: " + assemblyPath, ex);
			}
			catch (ArgumentException) { }

			try
			{
				File.Delete(xmlDocPath);
			}
			catch (IOException ex)
			{
				throw new InstallException("A file system exception has prevented the AutoInputProtection documentation file from being removed: " + xmlDocPath, ex);
			}
			catch (UnauthorizedAccessException ex)
			{
				throw new InstallException("The current user is not authorized to remove the AutoInputProtection documentation file: " + xmlDocPath, ex);
			}
			catch (NotSupportedException ex)
			{
				throw new InstallException("The AutoInputProtection documentation file cannot be removed: " + xmlDocPath, ex);
			}
			catch (ArgumentException) { }

			string tabFolder = Path.GetDirectoryName(assemblyPath);

			DirectoryInfo directory = new DirectoryInfo(tabFolder);

			if (directory.Exists && directory.GetFiles().Length == 0)
			{
				try
				{
					directory.Delete();
				}
				catch (IOException ex)
				{
					throw new InstallException("The custom control directory cannot be removed: " + tabFolder, ex);
				}
				catch (SecurityException ex)
				{
					throw new InstallException("The custom control directory cannot be removed: " + tabFolder, ex);
				}
			}
		}

		private void VisualStudioInstallCommunityControls2005(bool install)
		{
			VisualStudioInstallCommunityControls(install, 
				Environment.ExpandEnvironmentVariables(@"%VS80COMNTOOLS%..\IDE\devenv.exe"));
		}

		private void VisualStudioInstallCommunityControls2008(bool install)
		{
			VisualStudioInstallCommunityControls(install,
				Environment.ExpandEnvironmentVariables(@"%VS90COMNTOOLS%..\IDE\devenv.exe"));
		}

		private void VisualStudioInstallCommunityControls(bool install, string devenv)
		{
			try
			{
				// running this command again will also uninstall the controls from the toolbox
				Process.Start(devenv, "/command Tools.InstallCommunityControls");
			}
			catch (Win32Exception ex)
			{
				MessageBox.Show("An error occurred attempting to " + ((install) ? "install" : "uninstall") +
					" the AIP web control " + ((install) ? "into" : "from") + " Visual Studio: " + Environment.NewLine + Environment.NewLine +
					ex.Message + Environment.NewLine + Environment.NewLine + "The library " + ((install) ? "installation" : "uninstallation") +
					" will continue but you may have to " + ((install) ? "install" : "uninstall") + " the AIP web control " +
					((install) ? "into" : "from") + " Visual Studio yourself after it has completed.",
					((install) ? "Installation" : "Uninstallation") + " Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
			}
		}
		#endregion
	}
}